﻿[cmdletbinding(SupportsShouldProcess=$true,DefaultParameterSetName="current")]
param (
    [parameter(ParameterSetName="current")]
    [switch] $Current,
    
    [parameter(ParameterSetName="set_tag",Mandatory=$true)]
    [switch] $NewTag,

    [parameter(ParameterSetName="offline",Mandatory=$true)]
    [switch] $Local,
    [parameter(ParameterSetName="offline")]
    [string] $BranchName = "master",

    [parameter(ParameterSetName="set_tag",Mandatory=$true)]
    [parameter(ParameterSetName="offline",Mandatory=$true)]
    $Version,

    [parameter(ParameterSetName="set_tag",Mandatory=$true)]
    [string] $Description
    )

Set-StrictMode -Version Latest

function callGit {
    [cmdletbinding(PositionalBinding=$false)]
    param([parameter(ValueFromRemainingArguments=$true)] [string[]] $GitArgs) 

    & git.exe @GitArgs

    if ($LASTEXITCODE -ne 0) {
        throw "Git failure: $LASTEXITCODE"
    }
}

function callMsBuild {
    param([parameter(Mandatory=$true)] $Target,
          [parameter(Mandatory=$true)] $BranchName,
          [parameter(Mandatory=$true)] $Version)
   
    Write-Verbose "calling msbuild.exe -t:$Target -p:VersionNumber=$Version;Branch=$BranchName"
    msbuild.exe "-t:$Target" "-p:VersionNumber=$Version;Branch=$BranchName" | Write-Verbose

    if ($LASTEXITCODE -ne 0) {
        throw "Build failed ($LASTEXITCODE).  Aborting publish."
    }
}

function getBranch() {
    $l = @(callGit status --porcelain --branch --untracked-files=no)
    if ($l[0] -notmatch "## ([^.]*)(\.\.\.)?") {
        $l | Write-Host
        throw "Error - cannot parse branch status line"
    }
    return $Matches[1]
}

function getLatestTag([parameter(Mandatory=$true)]$BranchName) {
    callGit describe $branchName --tags
}

function tagBranch {
    param([parameter(Mandatory=$true)] $Description, [parameter(Mandatory=$true)] $Version)
    callGit tag --annotate --message $Description $Version
}

function confirm {
    [cmdletbinding(SupportsShouldProcess=$true,ConfirmImpact="High")]
    param([parameter(Mandatory=$true)]$Description, 
          [parameter(Mandatory=$true)]$Warning, 
          [parameter(Mandatory=$true)]
          [ref]
          [bool] $PrintOnly)

    $reason = [System.Management.Automation.ShouldProcessReason]::None
    $result = $PSCmdlet.ShouldProcess($Description, $Warning, "Confirm", [ref] $reason)

    if ($result) {
        $PrintOnly.Value = $false
        return $true
    } elseif ($reason -eq "WhatIf") {
        $PrintOnly.Value = $true
        return $true
    } else {
        return $false
    }
}

function publishBranch {
    [cmdletbinding(SupportsShouldProcess=$true,DefaultParameterSetName="current")]
    param (
        [parameter(ParameterSetName="current")]
        [switch] $Current,
    
        [parameter(ParameterSetName="set_tag",Mandatory=$true)]
        [switch] $NewTag,

        [parameter(ParameterSetName="offline",Mandatory=$true)]
        [switch] $Local,
        [parameter(ParameterSetName="offline")]
        [string] $BranchName = "master",

        [parameter(ParameterSetName="set_tag",Mandatory=$true)]
        [parameter(ParameterSetName="offline",Mandatory=$true)]
        $Version,

        [parameter(ParameterSetName="set_tag",Mandatory=$true)]
        [string] $Description
    )
    
    # Get branch and version information, if needed.

    switch ($PSCmdlet.ParameterSetName) {

        "current" {
            # fetch $version from GIT
            $BranchName = getBranch 
            $Version = getLatestTag -BranchName $BranchName
            break;
        }

        "set_tag" {
            $BranchName = getBranch
            [bool] $skipTag = $false
            if ((confirm "Create tag $Version on $BranchName branch"  "Create tag $Version on $BranchName branch" ([ref]$skipTag)) -eq $false) {
                return
            }

            break;
        }

        "offline" {
            break;
        }
    }

    switch ($BranchName) {
        "master" {$suffix = $Version; break}
        default  {$suffix = "{0}_{1}" -f $BranchName,$Version; break;}
    }

    Write-Warning "Building and publishing $BranchName version $suffix"


    # Rebuild the target before tagging
    Write-Host -ForegroundColor Cyan -Object "Building"
    callMsBuild -Target Rebuild -Version $suffix -BranchName $BranchName

    # Tag the branch, if necessary 
    if ($PSCmdlet.ParameterSetName -eq "set_tag") {
        Write-Host -ForegroundColor Cyan -Object "Tagging branch"
        if ($skipTag) {
            $PSCmdlet.ShouldProcess($BranchName, "create tag $Version ($Description)") | Out-Null
        } else {
            callGit tag --annotate --message "$Description" "$Version"
        }
    }

    # publish the zip file
    Write-Host -ForegroundColor Cyan -Object "Publishing"
    callMsBuild -Target Publish -Version $suffix -BranchName $BranchName
}

publishBranch @PSBoundParameters